#pragma once

#include "types.h"
#include "math/math.h"
#include "display/display-object.h"
#include "display/display-object-container.h"

namespace tmlib {

    namespace display {
        class DisplayObject;
    }

    namespace motion {

        namespace AnimationInfo
        {
            enum PropertyType
            {
                X = 0,
                Y,
                SCALE_X,
                SCALE_Y,
                ROTATION,
                BASE_X,
                BASE_Y,
                PROPERTY_TYPE_MAX,
            };
        }


        class Motion
        {
        public :
            union Source {
                struct {
                    f32 x;
                    f32 y;
                    f32 scaleX;
                    f32 scaleY;
                    f32 rotation;
                    f32 baseX;
                    f32 baseY;
                };
                f32 _list[AnimationInfo::PROPERTY_TYPE_MAX];
            };

        public :
            Motion() : _duration(32)
            {
                memset(_sourceList, 0, sizeof(_sourceList));
            }

            f32 getValue(u32 index, AnimationInfo::PropertyType type)
            {
                return _sourceList[index]._list[type];
            }

            Motion* setValue(u32 index, AnimationInfo::PropertyType type, f32 value)
            {
                _sourceList[index]._list[type] = value;
                return this;
            }

            Source getFilters(u32 time) {
                Source result;
                const f32 amount = (float)(time)/(float)(_duration);

                result.x = 
                    math::lerp(getValue(0, AnimationInfo::X),
                    getValue(1, AnimationInfo::X), amount);
                result.y = 
                    math::lerp(getValue(0, AnimationInfo::Y),
                    getValue(1, AnimationInfo::Y), amount);

                return result;
            };

            s32 duration() { return _duration; }
            Motion* duration(s32 duration) { _duration = duration; return this; }

        public :
            s32     _duration;
            Source  _sourceList[2];
        };


        class Animator : public EventDispatcher
        {
        public :
            Animator()
                : _time(0), _playingFlag(false), _target(NULL), _motion(NULL)
            {
                _motion = new Motion();
            }

            Animator(display::DisplayObject* target)
                : _time(0), _playingFlag(false), _target(target), _motion(NULL)
            {
                _motion = new Motion();
                _motion->setValue(0, AnimationInfo::X, target->x());
                _motion->setValue(0, AnimationInfo::Y, target->y());
            }

            Animator(void* data, display::DisplayObject* target)
                : _time(0), _playingFlag(false), _target(target), _motion(NULL)
            {
        
            }

            void setTime(s32 time)
            {
                _time = time;
                updateTarget();
            }

            void update()
            {
                if (isPlaying() == false) { return ; }

                setTime(_time + 1);
                dispatchEvent(events::EventInfo::MOTION_UPDATE);

                if (_time >= _motion->duration()) { end(); }
            }

            void updateTarget()
            {
                Motion::Source source =  _motion->getFilters(_time);

                _target->x(source.x);
                _target->y(source.y);
            }

            void play()
            {
                setPlayingFlag(true);
                setTime(0);
                dispatchEvent(events::EventInfo::MOTION_START);
            }

            void stop()
            {
                setPlayingFlag(false);
                setTime(0);
            }

            void end()
            {
                setPlayingFlag(false);
                setTime(_motion->duration());
                dispatchEvent(events::EventInfo::MOTION_END);
            }

            void pause()
            {
                setPlayingFlag(false);
            }

            void resume()
            {
                setPlayingFlag(true);
            }

            bool isPlaying() { return _playingFlag; }

            display::DisplayObject* target() { return _target; }
            Motion* motion()        { return _motion; }

            Animator* target(display::DisplayObject* target) {
                _target=target;
                _motion->setValue(0, AnimationInfo::X, target->x());
                _motion->setValue(0, AnimationInfo::Y, target->y());

                return this;
            }

        protected :
            void setPlayingFlag(bool flag) { _playingFlag = flag; }

        public :
            s32     _time;
            bool    _playingFlag;
            display::DisplayObject*  _target;
            Motion*         _motion;
        };
    
    }

}